﻿#region License
// MIT License
//
// Copyright(c) 2019-2025 ZhangYu
// https://gitee.com/zhangyu800/litebyte
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#endregion
#region Intro
// Purpose: Convert object to bytes
// Author: ZhangYu
// CreateDate: 2022-06-14
// LastModifiedDate: 2025-08-30
#endregion
using LiteByte.Converters;

namespace LiteByte {

    using LiteByte.Common;
    using LiteByte.Reflection;
    using System;
    using System.Collections;
    using System.Collections.Generic;

    /// <summary>
    /// <para> LiteByte数据写入器</para>
    /// <para> 把对象转换为字节 | Convert object to bytes</para>
    /// </summary>
    public class LBWriter : BaseTypeWriter {

        #region 构造方法 | Constructors
        public LBWriter() : base() { }
        public LBWriter(int capacity) : base(capacity) { }
        #endregion

        #region 基本类型数组 | Base type array
        private void WriteBaseTypeArray(TypeName elementType, Array array) {
            if (array == null) {
                // 空数组
                WriteBit1(true);
                return;
            }
            // 非空数组
            WriteBit1(false);
            // 写入长度
            int length = array.Length;
            WriteVInt32(length);
            // 预先分配空间
            EnsureCapacity((int)Math.Ceiling(length * CommonUtil.GetByteSize(elementType)));
            switch (elementType) {
                case TypeName.Bit1: WriteBaseTypeArray((bool[])array, WriteBit1); break;
                case TypeName.Bit2: WriteBaseTypeArray((byte[])array, WriteBit2); break;
                case TypeName.Bit3: WriteBaseTypeArray((byte[])array, WriteBit3); break;
                case TypeName.Bit4: WriteBaseTypeArray((byte[])array, WriteBit4); break;
                case TypeName.Bit5: WriteBaseTypeArray((byte[])array, WriteBit5); break;
                case TypeName.Bit6: WriteBaseTypeArray((byte[])array, WriteBit6); break;
                case TypeName.Bit7: WriteBaseTypeArray((byte[])array, WriteBit7); break;
                case TypeName.Bit8: WriteBaseTypeArray((byte[])array, WriteBit8); break;
                case TypeName.Bool: WritePrimitiveArray(array, 1); break;
                case TypeName.Char: WritePrimitiveArray(array, 2); break;
                case TypeName.UInt8: WritePrimitiveArray(array, 1); break;
                case TypeName.UInt16: WritePrimitiveArray(array, 2); break;
                case TypeName.UInt32: WritePrimitiveArray(array, 4); break;
                case TypeName.UInt64: WritePrimitiveArray(array, 8); break;
                case TypeName.Int8: WritePrimitiveArray(array, 1); break;
                case TypeName.Int16: WritePrimitiveArray(array, 2); break;
                case TypeName.Int32: WritePrimitiveArray(array, 4); break;
                case TypeName.Int64: WritePrimitiveArray(array, 8); break;
                case TypeName.Float16: WriteBaseTypeArray((Half[])array, WriteFloat16); break;
                case TypeName.Float32: WritePrimitiveArray(array, 4); break;
                case TypeName.Float64: WritePrimitiveArray(array, 8); break;
                case TypeName.VUInt16: WriteBaseTypeArray((ushort[])array, WriteVUInt16); break;
                case TypeName.VUInt32: WriteBaseTypeArray((uint[])array, WriteVUInt32); break;
                case TypeName.VUInt64: WriteBaseTypeArray((ulong[])array, WriteVUInt64); break;
                case TypeName.VInt16: WriteBaseTypeArray((short[])array, WriteVInt16); break;
                case TypeName.VInt32: WriteBaseTypeArray((int[])array, WriteVInt32); break;
                case TypeName.VInt64: WriteBaseTypeArray((long[])array, WriteVInt64); break;
                case TypeName.Decimal: WriteBaseTypeArray((decimal[])array, WriteDecimal); break;
                case TypeName.DateTime: WriteBaseTypeArray((DateTime[])array, WriteDateTime); break;
                case TypeName.VUnicode: WriteBaseTypeArray((string[])array, WriteVUnicode); break;
                case TypeName.ASCII: WriteBaseTypeArray((string[])array, WriteASCII); break;
                case TypeName.UTF8: WriteBaseTypeArray((string[])array, WriteUTF8); break;
                case TypeName.UTF16: WriteBaseTypeArray((string[])array, WriteUTF16); break;
                case TypeName.UTF32: WriteBaseTypeArray((string[])array, WriteUTF32); break;
                case TypeName.GBK: WriteBaseTypeArray((string[])array, WriteGBK); break;
                default: throw new Exception("WriteBaseTypeArray() Unknown base type:" + elementType);
            }
        }

        private void WriteBaseTypeArray<T>(T[] array, Action<T> writeElement) {
            for (int i = 0; i < array.Length; i++) {
                writeElement(array[i]);
            }
        }

        /// <summary> 写入原始类型数组 | Write primitive array to buffer </summary>
        /// <param name="array"> 源数组 | Source array</param>
        /// <param name="elementSize"> 元素大小 | element byte count</param>
        private void WritePrimitiveArray(Array array, int elementSize) {
            int byteCount = array.Length * elementSize;
            EnsureCapacity(byteCount);
            System.Buffer.BlockCopy(array, 0, buffer, byteIndex, byteCount);
            byteIndex += byteCount;
        }
        #endregion

        #region 基本类型列表 | Base type list
        private void WriteBaseTypeList(TypeName elementType, IList list) {
            if (list == null) {
                // 空列表
                WriteBit1(true);
                return;
            }
            // 非空列表
            WriteBit1(false);
            // 写入长度
            WriteVInt32(list.Count);
            // 预先分配空间
            EnsureCapacity((int)Math.Ceiling(list.Count * CommonUtil.GetByteSize(elementType)));
            switch (elementType) {
                case TypeName.Bit1: WriteBaseTypeList((List<bool>)list, WriteBit1); break;
                case TypeName.Bit2: WriteBaseTypeList((List<byte>)list, WriteBit2); break;
                case TypeName.Bit3: WriteBaseTypeList((List<byte>)list, WriteBit3); break;
                case TypeName.Bit4: WriteBaseTypeList((List<byte>)list, WriteBit4); break;
                case TypeName.Bit5: WriteBaseTypeList((List<byte>)list, WriteBit5); break;
                case TypeName.Bit6: WriteBaseTypeList((List<byte>)list, WriteBit6); break;
                case TypeName.Bit7: WriteBaseTypeList((List<byte>)list, WriteBit7); break;
                case TypeName.Bit8: WriteBaseTypeList((List<byte>)list, WriteBit8); break;
                case TypeName.Bool: WriteBaseTypeList((List<bool>)list, WriteBool); break;
                case TypeName.Char: WriteBaseTypeList((List<char>)list, WriteChar); break;
                case TypeName.UInt8: WriteBaseTypeList((List<byte>)list, WriteUInt8); break;
                case TypeName.UInt16: WriteBaseTypeList((List<ushort>)list, WriteUInt16); break;
                case TypeName.UInt32: WriteBaseTypeList((List<uint>)list, WriteUInt32); break;
                case TypeName.UInt64: WriteBaseTypeList((List<ulong>)list, WriteUInt64); break;
                case TypeName.Int8: WriteBaseTypeList((List<sbyte>)list, WriteInt8); break;
                case TypeName.Int16: WriteBaseTypeList((List<short>)list, WriteInt16); break;
                case TypeName.Int32: WriteBaseTypeList((List<int>)list, WriteInt32); break;
                case TypeName.Int64: WriteBaseTypeList((List<long>)list, WriteInt64); break;
                case TypeName.Float16: WriteBaseTypeList((List<Half>)list, WriteFloat16); break;
                case TypeName.Float32: WriteBaseTypeList((List<float>)list, WriteFloat32); break;
                case TypeName.Float64: WriteBaseTypeList((List<double>)list, WriteFloat64); break;
                case TypeName.VUInt16: WriteBaseTypeList((List<ushort>)list, WriteVUInt16); break;
                case TypeName.VUInt32: WriteBaseTypeList((List<uint>)list, WriteVUInt32); break;
                case TypeName.VUInt64: WriteBaseTypeList((List<ulong>)list, WriteVUInt64); break;
                case TypeName.VInt16: WriteBaseTypeList((List<short>)list, WriteVInt16); break;
                case TypeName.VInt32: WriteBaseTypeList((List<int>)list, WriteVInt32); break;
                case TypeName.VInt64: WriteBaseTypeList((List<long>)list, WriteVInt64); break;
                case TypeName.Decimal: WriteBaseTypeList((List<decimal>)list, WriteDecimal); break;
                case TypeName.DateTime: WriteBaseTypeList((List<DateTime>)list, WriteDateTime); break;
                case TypeName.VUnicode: WriteBaseTypeList((List<string>)list, WriteVUnicode); break;
                case TypeName.ASCII: WriteBaseTypeList((List<string>)list, WriteASCII); break;
                case TypeName.UTF8: WriteBaseTypeList((List<string>)list, WriteUTF8); break;
                case TypeName.UTF16: WriteBaseTypeList((List<string>)list, WriteUTF16); break;
                case TypeName.UTF32: WriteBaseTypeList((List<string>)list, WriteUTF32); break;
                case TypeName.GBK: WriteBaseTypeList((List<string>)list, WriteGBK); break;
                default: throw new Exception("WriteBaseTypeList() Unknown base type:" + elementType);
            }
        }

        private void WriteBaseTypeList<T>(List<T> list, Action<T> writeElement) {
            for (int i = 0; i < list.Count; i++) {
                writeElement(list[i]);
            }
        }
        #endregion

        #region 写入对象 | Write Object
        /// <summary> 写入任意类型 | Write any type </summary>
        public void WriteObject<T>(T obj) {
            WriteObject(obj, ConfigData.GetType(typeof(T).Name));
        }

        /// <summary> 写入任意类型 | Write any type </summary>
        public void WriteObject(object obj, TypeData lbType) {
            if (lbType.IsBaseType) {
                WriteBaseType(lbType.TypeCode, obj);
            } else if (lbType.IsArray) {
                WriteArray((Array)obj, lbType);
            } else if (lbType.IsList) {
                WriteList((IList)obj, lbType);
            } else if (lbType.IsHashMap) {
                WriteHashMap((IDictionary)obj, lbType);
            } else {
                WriteCustom(obj, lbType);
            }
        }

        /// <summary> 写入数组 | Write array </summary>
        public void WriteArray<T>(T[] array) {
            Type type = typeof(T[]);
            TypeData elementType = ConfigData.GetType(typeof(T).Name);
            TypeData lbType = new TypeData(type.Name, elementType);
            WriteArray(array, lbType);
        }

        /// <summary> 写入数组 | Write array </summary>
        private void WriteArray(Array array, TypeData lbType) {
            TypeData elementType = lbType.ElementType;
            if (elementType.IsBaseType) {
                WriteBaseTypeArray(elementType.TypeCode, array);
            } else {
                // 判断对象是否为默认值
                if (array == null) {
                    WriteBit1(true);
                } else {
                    WriteBit1(false);
                    WriteVUInt32((uint)array.Length);
                    for (int i = 0; i < array.Length; i++) {
                        WriteObject(array.GetValue(i), elementType);
                    }
                }
            }
        }

        /// <summary> 写入列表 | Write list </summary>
        public void WriteList<T>(List<T> list) {
            Type type = typeof(List<T>);
            List<TypeData> genericTypes = [ConfigData.GetType(typeof(T).Name)];
            TypeData lbType = new TypeData(type.Name, genericTypes, true, false);
            WriteList(list, lbType);
        }

        /// <summary> 写入列表 | Write list </summary>
        private void WriteList(IList list, TypeData lbType) {
            TypeData genericType = lbType.GenericTypes[0];
            if (genericType.IsBaseType) {
                WriteBaseTypeList(genericType.TypeCode, list);
            } else {
                // 判断对象是否为默认值
                if (list == null) {
                    WriteBit1(true);
                } else {
                    WriteBit1(false);
                    WriteVUInt32((uint)list.Count);
                    for (int i = 0; i < list.Count; i++) {
                        WriteObject(list[i], genericType);
                    }
                }
            }
        }

        /// <summary> 写入哈希表 | Write hash map </summary>
        public void WriteHashMap<K, V>(Dictionary<K, V> map) {
            Type type = typeof(Dictionary<K, V>);
            List<TypeData> genericTypes = [ConfigData.GetType(typeof(K).Name), ConfigData.GetType(typeof(V).Name)];
            TypeData lbType = new TypeData(type.Name, genericTypes, false, true);
            WriteHashMap(map, lbType);
        }

        /// <summary> 写入哈希表 | Write hash map </summary>
        private void WriteHashMap(IDictionary map, TypeData lbType) {
            // 判断对象是否为默认值
            if (map == null) {
                WriteBit1(true);
            } else {
                WriteBit1(false);
                WriteVUInt32((uint)map.Count);
                foreach (DictionaryEntry entry in map) {
                    WriteObject(entry.Key, lbType.GenericTypes[0]);
                    WriteObject(entry.Value, lbType.GenericTypes[1]);
                }
            }
        }

        /// <summary> 写入自定义类型 | Write custom type </summary>
        private void WriteCustom(object obj, TypeData lbType) {
            // 判断对象是否为默认值
            if (ReflectionUtil.IsDefaultValue(obj, lbType)) {
                WriteBit1(true);
            } else {
                WriteBit1(false);
                // 写入所有字段的值 | Write all fields value
                Type type = obj.GetType();
                List<FieldData> lbFields = lbType.Fields;
                IMemberWrapper[] wrappers = ReflectionUtil.GetMembersWrappers(type, lbFields);
                for (int i = 0; i < lbFields.Count; i++) {
                    FieldData lbField = lbFields[i];
                    TypeData lbFieldType = lbField.Type;
                    IMemberWrapper wrapper = wrappers[i];
                    try {
                        WriteObject(wrapper.GetValue(obj), lbFieldType);
                    } catch (Exception ex) {
                        throw new Exception($"LBConvert.WriteCustom({lbType}, {lbField.Name}) error!\n{ex}");
                    }
                }
            }
        }
        #endregion

    }

}